
# from exp_chain import *
import cProfile
import sys

from cnrp_experiment.exp_node import *
from cnrp_experiment.exp_config import GlobalDict


# 2021.10.29 CNRP test
# 模拟测试环境

# 以下不同类别函数调用级别从下至上


# ======================================================
# 通用运行函数 用于配置好节点之后运行试验
# 一般不需要改动
def run_exp(tx_num, progress=True):

    # 初始化节点抽奖字典
    node_active_dict = {}
    for k, v in GlobalDict.node_dict.items():
        node_active_dict[k] = v.degree_active

    bc = TestBlockChain()
    bc.create_genesis_block()
    bc.init_cur_block()

    # 模拟交易
    for j in range(tx_num):
        # 进度条
        if progress:
            print('\r[ %d / %d ]' % (j, tx_num), end='')
        active_node_id = luck_draw(node_active_dict)
        tx = GlobalDict.node_dict[active_node_id].produce_tx(GlobalDict.last_tx_id + 1)
        if not tx:
            continue
        bc.add_tx(tx)

    if progress:
        print('')


# 通用函数 清理全局变量 GlobalDict
def clear_global_dict():
    # 手动清空公共变量内存栈
    GlobalDict.node_dict = {}
    GlobalDict.chain_dict = {}
    GlobalDict.last_height = -1
    GlobalDict.last_tx_id = -1
    GlobalDict.cur_block = {}
    GlobalDict.time_consume = {'RP_total': 0.0, 'RP_block': {}}


# ======================================================

# 以下试验函数 具体实验配置


# 试验 1
# @param: node_num <list>: 各种类型的 node 数目列表 [base_node, evil_node]
#         tx_num <int>: tx 数目
def run_exp_1(node_num, tx_num):

    # 添加一般节点
    for i in range(1, node_num[0]+1):
        GlobalDict.node_dict[i] = TestNode(i)

    # 添加恶意节点 上传低质量数据
    for i in range(node_num[0]+1, node_num[0]+node_num[1]+1):
        _node = TestNode(i)
        _node.degree_data_quality = {0.2: 10, 0.7: 0}
        GlobalDict.node_dict[i] = _node

    # 运行
    run_exp(tx_num)

    # # 节点抽奖字典
    # node_active_dict = {}
    # for k, v in GlobalDict.node_dict.items():
    #     node_active_dict[k] = v.degree_active
    #
    # bc = TestBlockChain()
    # bc.create_genesis_block()
    # bc.init_cur_block()
    #
    # # 模拟交易
    # for j in range(tx_num):
    #     active_node_id = luck_draw(node_active_dict)
    #     tx = GlobalDict.node_dict[active_node_id].produce_tx(GlobalDict.last_tx_id + 1)
    #     bc.add_tx(tx)

    # return get_cnrp_value_package(GlobalDict.chain_dict)


# 试验 2
# @param: node_num <list>: 各种类型的 node 数目列表
#         tx_num <int>: tx 数目
def run_exp_2(node_num, tx_num):

    # 添加正常节点 1 ~ 10
    for i in range(1, node_num[0]+1):
        GlobalDict.node_dict[i] = TestNode(i)

    # 添加特殊节点：经常上传数据 11
    for i in range(node_num[0]+1, sum(node_num[:2])+1):
        _node = TestNodeBase(i)
        _node.degree_act_prefer = {'upload': 10, 'request': 2, 'share': 2, 'evaluate': 2}
        GlobalDict.node_dict[i] = _node

    # 添加特殊节点：上传的数据由好变坏 12
    for i in range(sum(node_num[:2])+1, sum(node_num[:3])+1):
        _node = TestNodeGoodToBad(i)
        # _node.degree_act_prefer = {'upload': 10, 'request': 2, 'share': 2, 'evaluate': 2}
        GlobalDict.node_dict[i] = _node

    # 添加特殊节点：从不上传数据 13
    for i in range(sum(node_num[:3])+1, sum(node_num[:4])+1):
        _node = TestNodeNoUpload(i)
        GlobalDict.node_dict[i] = _node

    # 添加特殊节点：经常上传数据 14
    for i in range(sum(node_num[:4]) + 1, sum(node_num[:5]) + 1):
        _node = TestNodeNoUploadToQuiet(i)
        GlobalDict.node_dict[i] = _node

    # 运行
    run_exp(tx_num)

    # # 节点抽奖字典
    # node_active_dict = {}
    # for k, v in GlobalDict.node_dict.items():
    #     node_active_dict[k] = v.degree_active
    #
    # bc = TestBlockChain()
    # bc.create_genesis_block()
    # bc.init_cur_block()
    #
    # # 模拟交易
    # for j in range(tx_num):
    #     # 进度条
    #     print('\r[ %d / %d ]' % (j, tx_num), end='')
    #     active_node_id = luck_draw(node_active_dict)
    #     tx = GlobalDict.node_dict[active_node_id].produce_tx(GlobalDict.last_tx_id + 1)
    #     if not tx:
    #         continue
    #     bc.add_tx(tx)
    #
    # print('')

    # return get_cnrp_value_package(GlobalDict.chain_dict)


# 试验 3
# @param: node_num <list>: 各种类型的 node 数目列表 [base_node, evil_node]
#         tx_num <int>: tx 数目
def run_exp_3(node_num, tx_num):
    # 添加一般节点
    for i in range(1, node_num[0]+1):
        # GlobalDict.node_dict[i] = TestNodeBase(i)
        GlobalDict.node_dict[i] = TestNode(i)
    # run:
    run_exp(tx_num)


# 实验4 按照 block num 模拟
def run_exp_4(node_num, block_num, progress=True):
    # 添加一般节点
    for i in range(1, node_num[0]+1):
        GlobalDict.node_dict[i] = TestNode(i)
    # run
    run_exp(block_num * BLOCK_TX_NUM, progress)


# 实验 5 恶意节点: 乱评价 + 上传低质量数据
def run_exp_5(node_num, block_num, progress=True):
    # 添加一般节点
    for i in range(1, node_num[0] + 1):
        GlobalDict.node_dict[i] = TestNode(i)
    # 添加恶意节点
    for i in range(node_num[0]+1, sum(node_num[:2])+1):
        GlobalDict.node_dict[i] = TestNodeBadEvaluateAndLowQualityData(i)

    # run
    run_exp(block_num * BLOCK_TX_NUM, progress)


# ======================================================

# 以下测试函数 包含：运行试验 + 绘图等


# web 返回接口
def run_exp_for_web():

    # # 手动清空公共变量内存栈
    # GlobalDict.node_dict = {}
    # GlobalDict.chain_dict = {}
    # GlobalDict.last_height = -1
    # GlobalDict.last_tx_id = -1
    # GlobalDict.cur_block = {}

    # 清理全局变量
    clear_global_dict()

    run_exp_2([10, 1, 1, 1, 1], 2000)
    # run_exp_1([10, 2], 1000)

    # return get_cnrp_value_package(GlobalDict.chain_dict)
    return GlobalDict

# -----------------------------------------


# 测试 1
def test1():

    # switch: 从文件中读取历史数据
    # read_switch = False

    time_start = time.time()

    # 可选: 从文件中读取历史实验数据
    # if read_switch:
    #     pass

    # main exp run:
    # run_exp_1([10, 2], 10000)
    run_exp_2([10, 1, 1, 1, 1], 30000)

    time_end = time.time()

    print('\nTime: ' + str(round(time_end - time_start, 2)) + 's')

    # plot:
    plot_line_2(unpack_cnrp_value(get_cnrp_value_package(GlobalDict.chain_dict)),
                [9, 10, 11, 12], ['Normal', 'Malicious', 'Lazy', 'Quiet'])


# 测试2 测试时间效率
def test2():

    # 不同测试配置 list
    test_config_list = [
        {'num_node': 10, 'num_tx': 10000},
        {'num_node': 10, 'num_tx': 20000},
        {'num_node': 10, 'num_tx': 30000},
        {'num_node': 10, 'num_tx': 40000},
        {'num_node': 10, 'num_tx': 50000},
        {'num_node': 10, 'num_tx': 60000},
        {'num_node': 10, 'num_tx': 70000},
        {'num_node': 10, 'num_tx': 80000},
        {'num_node': 10, 'num_tx': 90000},
        {'num_node': 10, 'num_tx': 100000},

        {'num_node': 20, 'num_tx': 10000},
        {'num_node': 20, 'num_tx': 20000},
        {'num_node': 20, 'num_tx': 30000},
        {'num_node': 20, 'num_tx': 40000},
        {'num_node': 20, 'num_tx': 50000},
        {'num_node': 20, 'num_tx': 60000},
        {'num_node': 20, 'num_tx': 70000},
        {'num_node': 20, 'num_tx': 80000},
        {'num_node': 20, 'num_tx': 90000},
        {'num_node': 20, 'num_tx': 100000},

        {'num_node': 40, 'num_tx': 10000},
        {'num_node': 40, 'num_tx': 20000},
        {'num_node': 40, 'num_tx': 30000},
        {'num_node': 40, 'num_tx': 40000},
        {'num_node': 40, 'num_tx': 50000},
        {'num_node': 40, 'num_tx': 60000},
        {'num_node': 40, 'num_tx': 70000},
        {'num_node': 40, 'num_tx': 80000},
        {'num_node': 40, 'num_tx': 90000},
        {'num_node': 40, 'num_tx': 100000},

        {'num_node': 80, 'num_tx': 10000},
        {'num_node': 80, 'num_tx': 20000},
        {'num_node': 80, 'num_tx': 30000},
        {'num_node': 80, 'num_tx': 40000},
        {'num_node': 80, 'num_tx': 50000},
        {'num_node': 80, 'num_tx': 60000},
        {'num_node': 80, 'num_tx': 70000},
        {'num_node': 80, 'num_tx': 80000},
        {'num_node': 80, 'num_tx': 90000},
        {'num_node': 80, 'num_tx': 100000},

        {'num_node': 100, 'num_tx': 10000},
        {'num_node': 100, 'num_tx': 20000},
        {'num_node': 100, 'num_tx': 30000},
        {'num_node': 100, 'num_tx': 40000},
        {'num_node': 100, 'num_tx': 50000},
        {'num_node': 100, 'num_tx': 60000},
        {'num_node': 100, 'num_tx': 70000},
        {'num_node': 100, 'num_tx': 80000},
        {'num_node': 100, 'num_tx': 90000},
        {'num_node': 100, 'num_tx': 100000},

        # 终极暴力测试
        # {'num_node': 1000, 'num_tx': 1000000},
    ]

    i = 0
    for config_dict in test_config_list:

        time_start = time.time()

        # main exp run:
        run_exp_3([config_dict['num_node']], 100000)

        time_end = time.time()

        clear_global_dict()
        i += 1

        print(str(i) + ':')
        print(config_dict)
        print('Time: ' + str(round(time_end - time_start, 2)) + 's')

        print('')


def test3():
    time_start = time.time()

    cProfile.run("run_exp_3([4000], 100000)")

    time_end = time.time()
    print('Time: ' + str(round(time_end - time_start, 2)) + 's')

    print(GlobalDict.time_consume)


# 测试3 绘图 时间效率
def test4():
    labels = ['G1', 'G2', 'G3', 'G4', 'G5']
    men_means = [20, 34, 30, 35, 27]
    women_means = [25, 32, 34, 20, 25]

    x = np.arange(len(labels))  # the label locations
    width = 0.35  # the width of the bars

    fig, ax = plt.subplots()
    rects1 = ax.bar(x - width / 2, men_means, width, label='Men')
    rects2 = ax.bar(x + width / 2, women_means, width, label='Women')

    # Add some text for labels, title and custom x-axis tick labels, etc.
    ax.set_ylabel('Scores')
    ax.set_title('Scores by group and gender')
    # ax.set_xticklabels(labels)
    ax.legend()

    ax.bar_label(rects1, padding=3)
    ax.bar_label(rects2, padding=3)

    fig.tight_layout()

    plt.show()


# 变量所占空间大小（真实性存疑）
# 函数 sys.getsizeof 单位: Byte 真实性不准确
def test5():
    # main exp run:
    run_exp_3([20], 10000)
    print(len(GlobalDict.chain_dict))
    print(sys.getsizeof(GlobalDict.chain_dict))
    # print(sys.getsizeof(GlobalDict.chain_dict[0]))


    # print('Every Block Size:')
    # for _ in range(GlobalDict.last_height+1):
    #     print(sys.getsizeof(GlobalDict.chain_dict[_]))
    # print('======================')

    print(sys.getsizeof(GlobalDict.chain_dict[GlobalDict.last_height]))
    print('')
    # print(sys.getsizeof(GlobalDict.node_dict))

    pass


def test6():

    # 不同测试配置 list
    test_config_list = [
        {'num_node': 10, 'num_block': 100},
        {'num_node': 100, 'num_block': 100},
        {'num_node': 200, 'num_block': 100},
        {'num_node': 500, 'num_block': 100},
        {'num_node': 1000, 'num_block': 100},
        {'num_node': 2000, 'num_block': 100},
        {'num_node': 3000, 'num_block': 100},
        {'num_node': 4000, 'num_block': 100},
        {'num_node': 5000, 'num_block': 100},
    ]

    time_start = time.time()
    i = 1
    for config_dict in test_config_list:
        # main exp run:
        run_exp_4([config_dict['num_node']], 100, False)

        # print(str(i) + ':')
        # print(config_dict['num_node'])
        print(GlobalDict.time_consume['RP_total'])
        # print('')

        clear_global_dict()

    time_end = time.time()
    print('Time: ' + str(round(time_end - time_start, 2)) + 's')


# 恶意节点比例对全局交易成功率的影响
def test7():
    pass


# ---------------------------------------------------------------------


# test main:
if __name__ == "__main__":
    # test1()
    # test2()
    # test3()
    # test4()
    # test5()
    # test6()

    pass

